package main

import "fmt"

/*
*
接收者为值类型或指针类型，决定了调用方法时，方法对实例是值操作还是指针操作。

	若接收者为值类型，那么无论是值还是指针调用该方法，方法操作的都是对象的副本。
	若接收者为指针类型，那么无论用值还是指针调用该方法，方法操作的都是对象的指针。
*/

//// 结构体
//type User struct {
//	Name  string
//	Email string
//}
//
//// 方法
//func (u User) Notify() { //接收者是User类型的一个值
//	fmt.Printf("%v : %v \n", u.Name, u.Email)
//}
//func main() {
//	// 值调用方法
//	u1 := User{"golang", "golang@golang.com"}
//	u1.Notify()
//	// 指针调用方法
//	u2 := User{"go", "go@go.com"}
//	u3 := &u2
//	u3.Notify() //这两行可以直接写成(&u2).Notify()
//}

/**
解释：
首先我们定义了一个叫做User的结构体类型，然后定一个一个该类型的方法叫做Notify ,该方法的接受者是一个User类型的值，
要调用Notify方法我们需要一个User类型的值或者指针。

当方法的接收者是值类型而我们使用指针类型调用方法时，Go会自己调整使得调用可以被执行，但是要注意！当方法定义中的接受者不是一个指针，而是值类型时，
该方法操作对应接受者值的副本（意思就是即使你使用了实例的指针调用函数，但是方法的接受者是值类型，所以函数内部操作还是对副本的操作，而不是指针操作）
*/

////9_结构体
//type User struct {
//	Name  string
//	Email string
//}
//
////方法
//func (u *User) Notify() {
//	fmt.Printf("%v : %v \n", u.Name, u.Email)
//}
//func main() {
//	// 值调用方法
//	u1 := User{"golang", "golang@golang.com"}
//	u1.Notify()
//	// 指针调用方法
//	u2 := User{"go", "go@go.com"}
//	u3 := &u2
//	u3.Notify()
//}

/**
注意当方法的接受者类型是指针时，即使使用值来调用方法，方法内部也是对指针的操作。
方法不过是一种特殊的函数，只需要将其还原就知道接受者为 T和 *T的差别
*/

type Data struct {
	x int
}

func (self Data) ValueTest() { //接收者为值类型。相当于函数 func ValueTest(self Data);
	fmt.Printf("Value: %p\n", &self)
}

func (self *Data) PointerTest() { //接收者为指针类型。相当于函数 func PointerTest(self *Data);
	fmt.Printf("Pointer: %p\n", self)
}

func main() {
	d := Data{}
	p := &d
	fmt.Printf("Data: %p\n", p)

	//都是复制引用
	d.ValueTest() //值调用
	p.ValueTest() //指针调用

	//都是地址引用
	d.PointerTest() //值调用
	p.PointerTest() //指针调用
}

/**
总结：
是值引用还是地址引用取决于定义方法时接受者的类型。
如果定义时，接受者是值类型，那么调用该方法时操作的就是值的副本。
如果定义时，接受者是指针类型那么调用该方法时，操作的就是指针。
与调用方法无关（与用实例的值还是指针来调用方法无关）
*/
